package com.sduept.i1;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.client.intf.TioClientHandler;
import org.tio.core.ChannelContext;
import org.tio.core.Tio;
import org.tio.core.TioConfig;
import org.tio.core.exception.TioDecodeException;
import org.tio.core.intf.Packet;

import com.sduept.i1.model.Apdu;
import com.sduept.i1.model.MonitorPacket;
import com.sduept.i1.parse.Decoder;
import com.sduept.tool.CrcUtil;

public class I1ClientHandler implements TioClientHandler {    
	private static Logger logger = LoggerFactory.getLogger(I1ClientHandler.class);
    /**
     * 解码：把接收到的ByteBuffer，解码成应用可以识别的业务消息包
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */

    @Override
    public MonitorPacket decode(ByteBuffer buffer, int limit, int position, int readableLength, ChannelContext channelContext) throws TioDecodeException {
        if(readableLength < 4) {
            return null;
        }

        buffer.order(ByteOrder.LITTLE_ENDIAN);
        int startHeader = buffer.getShort() & 0xFFFF;
        if(startHeader != MonitorPacket.FRAME_HEADER) {
            logger.error("客户端:开始帧头不对,startHeader:{}", startHeader);
            Tio.close(channelContext, "开始帧头不对");
            return null;
        }
        int msgLen = buffer.getShort();

        if(readableLength/*可读字节数*/ - 4/*已读字节数*/ < 23 + msgLen) {
            return null;
        }
        byte[] idBytes = new byte[17];
        buffer.get(idBytes);
        String identification = new String(idBytes);
        logger.info("identification: {}",identification);
        // 注意，输出报文，会影响报文解析，调试才能打开
        //logger.info("recv: " + PrintTool.bytes2hex(buffer.array(), position, msgLen + 27));

        MonitorPacket monitorPacket = new MonitorPacket();

        monitorPacket.identification = identification;
        monitorPacket.inApdu.msgLen = msgLen;
        monitorPacket.inApdu.frameType = buffer.get() & 0xFF;
        monitorPacket.inApdu.msgType = buffer.get() & 0xFF;
        monitorPacket.inApdu.frameNo = buffer.get() & 0xFF;
        if(msgLen > 0) {
            monitorPacket.inApdu.elements = new byte[msgLen];
            buffer.get(monitorPacket.inApdu.elements);
        } else {
            monitorPacket.inApdu.elements = null;
        }

        int crc16 = buffer.getShort() & 0xFFFF;
        int crcCheck = CrcUtil.calcCrc16(buffer.array(), position + 2, msgLen + 22);
        if(crc16 != crcCheck) {
            Tio.close(channelContext, "CRC16校验不对");
            return null;
        }
        int frameEnd = buffer.get() & 0xFF;
        if(frameEnd != MonitorPacket.FRAME_END) {
            Tio.close(channelContext, "结束帧尾不对");
            return null;
        }

        channelContext.set(I1Server.MONITOR_PACKET_KEY, monitorPacket);

        return monitorPacket;
    }
    /**
     * 编码：把业务消息包编码为可以发送的ByteBuffer
     * 总的消息结构：消息头 + 消息体
     * 消息头结构：    4个字节，存储消息体的长度
     * 消息体结构：   对象的json串的byte[]
     */
    @Override
    public ByteBuffer encode(Packet packet, TioConfig tioConfig, ChannelContext channelContext) {
        try {
            MonitorPacket monitorPacket = (MonitorPacket) packet;
            Apdu outApdu = monitorPacket.outApduQueue.poll();
            ByteBuffer byteBuffer = ByteBuffer.allocate(27 + outApdu.msgLen);
            byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
            byteBuffer.putShort(MonitorPacket.FRAME_HEADER);
            byteBuffer.putShort((short) (outApdu.msgLen & 0xFFFF));
            byteBuffer.put(monitorPacket.identification.getBytes());
            byteBuffer.put((byte) (outApdu.frameType & 0xFF));
            byteBuffer.put((byte) (outApdu.msgType & 0xFF));
            byteBuffer.put((byte) (outApdu.frameNo & 0xFF));
            
            if (outApdu.msgLen > 0) {
                byteBuffer.put(outApdu.elements);
            }
            
            int crc16 = CrcUtil.calcCrc16(byteBuffer.array(), 2, 22 + outApdu.msgLen);
            byteBuffer.putShort((short) (crc16 & 0xFFFF));

            logger.info("客户端:CRC16校验,msgLen={}-crc16={}", outApdu.msgLen, crc16);
            byteBuffer.put((byte) MonitorPacket.FRAME_END);

//          logger.info("send: {}", PrintTool.bytes2hex(byteBuffer.array()));
            byteBuffer.position(0);
            return byteBuffer;
        } catch (Exception e) {
            logger.error("encode异常", e);
            return null;
        }
    }
    /**
     * 处理消息
     */
    @Override
    public void handler(Packet packet, ChannelContext channelContext) throws Exception {
        MonitorPacket monitorPacket = (MonitorPacket) packet;

        switch (monitorPacket.inApdu.frameType) {
            case 0x01: //监测数据报文
                logger.info("收到监测数据报文");
                break;
            case 0x03:
            	logger.info("收到控制数据报");
            	
//            	Decoder.onControlRequest(monitorPacket, channelContext);
            	break;
            case 0x04: {//控制响应报文
                    logger.info("收到控制响应报文");
                    Apdu inApdu = new Apdu();
                    inApdu.msgLen = monitorPacket.inApdu.msgLen;
                    inApdu.frameType = monitorPacket.inApdu.frameType;
                    inApdu.msgType = monitorPacket.inApdu.msgType;
                    inApdu.frameNo = monitorPacket.inApdu.frameNo;
                    inApdu.elements = new byte[inApdu.msgLen];
                    System.arraycopy(monitorPacket.inApdu.elements, 0, inApdu.elements, 0, inApdu.msgLen);
                    if (monitorPacket.inApdu.msgType == 0xA8 || monitorPacket.inApdu.msgType == 0xA9 || monitorPacket.inApdu.msgType == 0xAA || monitorPacket.inApdu.msgType == 0xAB) {
                        monitorPacket.firmBlockingQueue.put(inApdu);
                    } else if (monitorPacket.inApdu.msgType == 0xFA) {
                        monitorPacket.devConfBlockingQueue.put(inApdu);
                    } else if (monitorPacket.inApdu.msgType == 0xA6) {
                        monitorPacket.remoteCtlBlockingQueue.put(inApdu);
                    }
                }
                break;
            case 0x05: //远程图像数据报文
                logger.info("收到远程图像数据报文");
                Decoder.onRemoteImageDataPacket(monitorPacket, channelContext);
                break;
            case 0x07: // 收到图像控制报
            	logger.info("收到远程图像控制报文");
//            	Decoder.onImageControlPacket(monitorPacket, channelContext);
            case 0x08: {//图像控制响应报文
                    logger.info("收到图像控制响应报文");
                    Apdu inApdu = new Apdu();
                    inApdu.msgLen = monitorPacket.inApdu.msgLen;
                    inApdu.frameType = monitorPacket.inApdu.frameType;
                    inApdu.msgType = monitorPacket.inApdu.msgType;
                    inApdu.frameNo = monitorPacket.inApdu.frameNo;
                    inApdu.elements = new byte[inApdu.msgLen];
                    System.arraycopy(monitorPacket.inApdu.elements, 0, inApdu.elements, 0, inApdu.msgLen);
                    if (monitorPacket.inApdu.msgType == 0xB3) {
                        monitorPacket.remoteCtlBlockingQueue.put(inApdu);
                    }
                }
                break;
            case 0x09: //工作状态报文
                logger.info("收到工作状态报文");
                Decoder.onWorkStatePacket(monitorPacket, channelContext);
                break;
            case 0x0A: // 工作状态响应报
            	logger.info("收到工作状态响应报文");
            	Decoder.onWorkStateResponsePacket(monitorPacket, channelContext);
            	break;
            default:
                logger.error("收到未识别的帧类型, frameType={}", monitorPacket.inApdu.frameType);
                break;
        }
    }
    /**
     * 此方法如果返回null，框架层面则不会发心跳；如果返回非null，框架层面会定时发本方法返回的消息包
     */


    @Override
    public MonitorPacket heartbeatPacket(ChannelContext channelContext) {
        return null;
    }
}

